#include "CatsManager.h"
#include "SimpleAudioEngine.h"
#include "Character/Character.h"
#include "Map/GameMap.h"
#include "Levels/CatSpawnScript.h"
#include "Levels/CatSpawnSet.h"
#include "Cats/BaseCat.h"
#include "Cats/FatCat.h"
#include "Cats/OlympicCat.h"
#include "Cats/NinjaCat.h"
#include "Cats/WitchCat.h"
#include "Cats/PunkCat.h"
#include "Cats/BounceCat.h"
#include "Cats/CatSpriteLoader.h"
#include "Supporting/Enums/EnumCatState.h"
#include "Supporting/Enums/EnumLevelType.h"
#include "Supporting/Enums/EnumCatType.h"
#include "GameManager.h"

using namespace cocos2d;
using namespace CocosDenshion;

//========== CONSTRUCTOR ==========

CatsManager::CatsManager()
{
	_cats = 0;
	_addCats = 0;

	_pSpawnScript = 0;
	_pDirectorSpawnScript = 0;

}

void CatsManager::init(int levelType, GameMap* pMapRef, Character* pCharRef, GameManager* pGmRef)
{
	//REFERENCES
	_pMapRef = pMapRef;
	_pCharRef = pCharRef;
	_pGmRef = pGmRef;

	//SPAWN
	_pSpawnScript = 0;
	_pDirectorSpawnScript = 0;

	//CAT
	if (_cats != 0)
		_cats->release();
	_cats = new CCArray();
	if (_addCats != 0)
		_addCats->release();
	_addCats = new CCArray();

	//OTHERS
	_levelType = levelType;
	_directorObstacleHitChance = INIT_DIRECTOR_VALUE;
	_directorSpeedMultiplier = INIT_DIRECTOR_VALUE;

	//PRELOAD ALL CAT SPRITES
	CatSpriteLoader::preloadSpriteSheets();
}
//========== DESTRUCTOR ==========

CatsManager::~CatsManager()
{
	_pMapRef = 0;
	_pCharRef = 0;
	_pGmRef = 0;

//	CCObject* cat = 0;
//	CCARRAY_FOREACH(_cats, cat)
//	{
//		BaseCat* pCat = dynamic_cast<BaseCat*>(cat);
//		delete pCat;
//	}

	_cats->release();
	_cats = 0;
	//delete _cats;

	_addCats->release();
	_addCats = 0;

	//script pointed by _pSpawnScript will be cleaned up by LEVEL class
	delete _pSpawnScript;
	_pSpawnScript = 0;

	if (_pDirectorSpawnScript)
		delete _pDirectorSpawnScript;
	_pDirectorSpawnScript = 0;
}

//========== FUNCTIONS ==========
void CatsManager::update(float timeDiff)
{
	//remove cats that are dead / marked for deletion
	removeDeadCats();

	//add extra cats generated
	addAdditionalCats();

	//update all cats in the game
	updateAllCats(timeDiff);

	//check spawn script
	runSpawnScript(_pSpawnScript, timeDiff);

	//check additional spawn script
	runSpawnScript(_pDirectorSpawnScript, timeDiff);

}

/*!
    @function addAdditionalCats
    @result		transfer cats from _addCats into the screen and _cats
 */
void CatsManager::addAdditionalCats()
{
	if (_addCats != 0 && _addCats->count() > 0)
	{
		_cats->addObjectsFromArray(_addCats);
		_addCats->removeAllObjects();
	}
}

/*!
    @function removeDeadCats
    @result		remove all cats marked with CAT_DEAD state from the array
    			remove dead cats from the screen
 */
void CatsManager::removeDeadCats()
{
	//remove any cats that is marked for deletion
	CCObject* cat = 0;
	CCARRAY_FOREACH(_cats, cat)
	{
		BaseCat* pCat = (BaseCat*)(cat);

		if (pCat->getCurrentCatState() == CAT_DEAD)
		{
			pCat->stopAllActions();
			_pGmRef->removeChild(pCat);

			_cats->removeObject(pCat, true);
		}
	}

}

/*!
    @function updateAllCats
    @param		timeDiff:float
				time difference between the last update call
    @result		calls the update function of all the cats currently spawned
 */
void CatsManager::updateAllCats(float timeDiff)
{
	//update all cats
	CCObject* cat = 0;
	CCARRAY_FOREACH(_cats, cat)
	{
		BaseCat* pCat = dynamic_cast<BaseCat*>(cat);
		pCat->update(timeDiff);
	}
}

int CatsManager::checkCatTouchCollision(CCPoint location){

	int hits = 0;

	CCObject* cat = 0;
	CCARRAY_FOREACH(_cats, cat)
	{
		BaseCat* pCat = dynamic_cast<BaseCat*>(cat);
		if (pCat == 0)
			continue;
		if( pCat->isDead() || pCat->isInjured() || pCat->isSwiped() )// && pCat->isClicked() ) //pCat->getCatState() == CAT_INJURED
			continue;

		if( pCat->boundingBox().containsPoint(location) )
		{
			pCat->setIsSwiped(true);

			//if( pCat->isDead() )
				hits += 1;
		}
	}

	return hits;
}

int CatsManager::damageAllSwipedCats(){
	CCObject* cat = 0;

	int basecatkilled = 0;
	int basecatkilledscore = 0;
	int basecatnotkilledscore = 0;

	CCARRAY_FOREACH(_cats, cat)
	{
		BaseCat* pCat = dynamic_cast<BaseCat*>(cat);
		if (pCat != 0 && pCat->isSwiped())
		{
			pCat->reduceHealth(1, INJURY_NORMAL);
			if( pCat->getHealth()-1 <= 0 )
			{
				basecatkilledscore += pCat->getBaseHealth();
				basecatkilled += 1;
			}
			else
			{
				basecatnotkilledscore += pCat->getBaseHealth();
			}
			pCat->setIsSwiped(false);
		}
	}

	return basecatkilled*basecatkilledscore + basecatnotkilledscore;
}

/*!
    @function runSpawnScript
    @param		script:CatSpawnScript*
    			the reference to script to run
    @param		timeDiff:float
				time difference between the last update call
    @result		loop through the current script and attempt to spawn cats if any
 */
void CatsManager::runSpawnScript(CatSpawnScript* script, float timeDiff)
{
	if (script == 0)
		return;

	//retrieve array of cat types to be added if any
	CCArray* pCatTypesToSpawn = script->updateAndCheckForSpawns(timeDiff);;

	//loop through all new cats to spawn into the game
	CCObject* catType = 0;
	CCARRAY_FOREACH(pCatTypesToSpawn, catType)
	{
		CCInteger* catTypeInt = dynamic_cast<CCInteger*>(catType);
		//create cat
		BaseCat* newCat = createCat(catTypeInt->getValue());
		//set random starting position
		newCat->setPosition(_pMapRef->getRandomCatStartPoint());
		//set new obstacle hit chance and speed multiplier
		if( _directorObstacleHitChance != INIT_DIRECTOR_VALUE ) newCat->setObstacleHitChance(_directorObstacleHitChance);
		if( _directorSpeedMultiplier != INIT_DIRECTOR_VALUE ) newCat->setSpeedMultiplier(_directorSpeedMultiplier);
		//add to the reference and screen
		_cats->addObject(newCat);
		_pGmRef->addChild(newCat);

		//clean up the integer array
		catTypeInt->release();
		catTypeInt = 0;
	}
	//clean up
	pCatTypesToSpawn->release();
	pCatTypesToSpawn = 0;
}

/*!
    @function createCat
    @param		catType:int
				the type of cat to spawn
    @result		returns a BaseCat* pointer to the created cat
 */
BaseCat* CatsManager::createCat(int catType)
{
	BaseCat* cat;
	switch (catType)
	{
	case BOUNCE_CAT:
		cat = BounceCat::createCat();
		break;

	case FAT_CAT:
		cat = FatCat::createCat();
		break;

	case NINJA_CAT:
		cat = NinjaCat::createCat();
		break;

	case OLYMPIC_CAT:
		cat = OlympicCat::createCat();
		break;

	case PEW_CAT:
		cat = BaseCat::createCat();
		break;

	case PUNK_CAT:
		cat = PunkCat::createCat();
		break;

	case WITCH_CAT:
		cat = WitchCat::createCat();
		break;

	default: //BASE_CAT
		cat = BaseCat::createCat();
		break;
	}
	return cat;
}

void CatsManager::addCat(BaseCat* cat)
{
	if (cat == 0)
		return;
	//add cats to game
	if (_addCats == 0)
		_addCats = new CCArray();
	_addCats->addObject(cat);
	_pGmRef->addChild(cat);
}

/*!
    @function setLevelSpawnScript
    @param		pCatSpawnScript: CatSpawnScript*
				the spawn script to set this cat manager to spawn
    @result		sets the current spawn script to the provided CatSpawnScript
 */
void CatsManager::setLevelSpawnScript(CatSpawnScript* pCatSpawnScript)
{
	_pSpawnScript = pCatSpawnScript;
}

/*!
    @function insertDirectorSpawnSet
    @param		pCatSpawnSet: CatSpawnSet*
    			the spawn set to insert into the director's spawn script
    @result		adds the specified spawn set into the director's spawn script.
 */
void CatsManager::insertDirectorSpawnSet(CatSpawnSet* pCatSpawnSet)
{
	if (_pDirectorSpawnScript == 0)
		_pDirectorSpawnScript = new CatSpawnScript(0);

	_pDirectorSpawnScript->addSpawnSet(pCatSpawnSet);
}

void CatsManager::updateDirectorObstacle(float obstacleHitChance)
{
	//TODO: Verify correctness
	//update all cats
	if( obstacleHitChance < 0.0 || obstacleHitChance > 100.0 ) return;

	// New cats must use new values
	_directorObstacleHitChance = obstacleHitChance;

	// Update all current cats
	CCObject* cat = 0;

	CCARRAY_FOREACH(_cats, cat)
	{
		BaseCat* pCat = (BaseCat*) (cat);
		pCat->setObstacleHitChance(obstacleHitChance);
	}
}

void CatsManager::updateDirectorSpeed(float speedMultiplier)
{
	//TODO
	if( speedMultiplier > 0 )
	{
		// New cats must use new values
		_directorSpeedMultiplier = speedMultiplier;

		// Update all current cats
		CCObject* cat = 0;

		CCARRAY_FOREACH(_cats, cat)
		{
			BaseCat* pCat = (BaseCat*) (cat);
			pCat->setSpeedMultiplier( pCat->getSpeedMultiplier() * speedMultiplier);
		}
	}
}



